import re
from datetime import datetime
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import Tk, Button, Label, filedialog, Frame

# 设置中文字体
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 使用黑体
plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题


def analyze_logs(file_path):
    # 读取日志文件
    with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
        logs = f.readlines()

    # 1. 数据预处理
    log_dtype = np.dtype(
        [
            ("remote_addr", "U20"),  # 远程IP地址
            ("time_local", "datetime64[s]"),  # 请求时间
            ("request", "U100"),  # 请求路径
            ("status", "i4"),  # HTTP状态码
            ("body_bytes_sent", "i4"),  # 发送字节数
        ]
    )
    parsed_logs = np.zeros(len(logs), dtype=log_dtype)

    # 正则表达式匹配日志行
    log_pattern = re.compile(
        r'(?P<remote_addr>\S+) - - \[(?P<time_local>[^\]]+)\] "(?P<request>[^"]+)" (?P<status>\d{3}) (?P<body_bytes_sent>-|\d+)'
    )

    # 解析日志文件，提取关键信息
    for i, log in enumerate(logs):
        match = log_pattern.match(log)

        if match:
            remote_addr = match.group("remote_addr")
            time_local_str = match.group("time_local")
            request = match.group("request")
            status = int(match.group("status"))
            body_bytes_sent = match.group("body_bytes_sent")
            body_bytes_sent = int(body_bytes_sent) if body_bytes_sent != "-" else 0

            # 去掉时区部分
            time_local_str = time_local_str[:-6]  # 去掉最后的时区信息（例如 -0400）

            # 解析日期和时间
            dt_object = datetime.strptime(time_local_str, "%d/%b/%Y:%H:%M:%S")

            parsed_logs[i] = (
                remote_addr,  # 远程IP地址
                np.datetime64(dt_object),  # 请求时间
                request,  # 请求路径
                status,  # HTTP状态码
                body_bytes_sent,  # 发送字节数
            )

    # 2. 请求数量统计
    dates = np.array(parsed_logs["time_local"].astype("datetime64[D]"))
    unique_dates, counts = np.unique(dates, return_counts=True)

    # 3. 状态码分布
    unique_status, status_counts = np.unique(parsed_logs["status"], return_counts=True)

    # 清空之前的图表
    for widget in frame_plot.winfo_children():
        widget.destroy()

    # 可视化请求数量统计
    fig1, ax1 = plt.subplots(figsize=(6, 4))
    ax1.bar(unique_dates.astype(str), counts, color="skyblue")
    ax1.set_xlabel("日期")
    ax1.set_ylabel("请求数量")
    ax1.set_title("每天的请求数量统计")
    ax1.tick_params(axis="x", rotation=45)

    # 将图表添加到 Tkinter 窗口
    canvas1 = FigureCanvasTkAgg(fig1, master=frame_plot)
    canvas1.draw()
    canvas1.get_tk_widget().pack()

    # 可视化状态码分布
    fig2, ax2 = plt.subplots(figsize=(6, 4))
    ax2.pie(status_counts, labels=unique_status, autopct="%1.1f%%", startangle=140)
    ax2.axis("equal")  # 使饼图为圆形
    ax2.set_title("HTTP 状态码分布")

    # 将图表添加到 Tkinter 窗口
    canvas2 = FigureCanvasTkAgg(fig2, master=frame_plot)
    canvas2.draw()
    canvas2.get_tk_widget().pack()


def select_file():
    file_path = filedialog.askopenfilename(
        filetypes=[("Log Files", "*.log"), ("All Files", "*.*")]
    )
    if file_path:
        analyze_logs(file_path)


# 创建 Tkinter 窗口
root = Tk()
root.title("Nginx 日志分析工具")

# 创建主框架
frame_main = Frame(root)
frame_main.pack(side="left", padx=10, pady=10)

# 创建按钮和标签
label = Label(frame_main, text="请选择 Nginx 日志文件进行分析")
label.pack(pady=10)

button = Button(frame_main, text="选择日志文件", command=select_file)
button.pack(pady=20)

# 创建图表框架
frame_plot = Frame(root)
frame_plot.pack(side="right", padx=10, pady=10)

# 运行 Tkinter 主循环
root.mainloop()
